---
sidebar_position: 1
title: useHotkeys
---

import { useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";

# useHotkeys API

Function signature:

```ts
function useHotkeys<T extends Element>(
  keys: string,
  callback: (event: KeyboardEvent, handler: HotkeysEvent) => void,
  options: Options = {},
  deps: any[] = []
): React.MutableRef<T | null>
```

***

## Arguments

### `keys`

```ts
keys: string
```

Set the keystrokes we want the hook to listen to. We can use single or multiple keys,
modifier combinations, etc. See [this guide](https://github.com/jaywcjlove/hotkeys/#defining-shortcuts) of the underlying
hotkeys package for all possible shortcuts.

#### Listening to all keys

```jsx
useHotkeys('*', (_, handler) => alert(handler.key))
```

#### Using modifiers

```jsx
useHotkeys('ctrl+s, shift+w', () => alert('We\'re using modifiers now!'))
```

:::info Using the alt modifier
The alt modifier has its own special handling. To check if an altKey was pressed we need to listen to all keys and check
for the alt modifier in the event object:

```jsx
useHotkeys('*', e => {
  console.log('Pressed alt modifier', e.altKey);
  console.log('Pressed key', e.key)
})
```

This will change in the upcoming v4.
:::

#### Using F keys

```jsx
useHotkeys('f5', () => alert('F5 was pressed'))
```

#### Using multiple keys

```jsx
useHotkeys('w, a, s, d', () => alert('Player moved!'))
```

:::tip Differentiating between multiple possible keys

If we use a combination of possible keys that use the same hook, we can use `handler.key` to check which key the user
pressed.

```jsx
useHotkeys('ctrl+a, shift+b, r, f', (_, handler) => {
  switch (handler.key) {
    case 'ctrl+a': alert('You pressed ctrl+a!');
      break;
    case 'ctrl+b': alert('You pressed ctrl+b!');
      break;
    case 'r': alert('You pressed r!');
      break;
    case 'f': alert('You pressed f!');
      break;
  }
})
```

:::

***

### `callback`

```ts
callback: (event: KeyboardEvent, handler: HotkeysEvent) => void
```

Gets executed when the defined keystroke gets hit by the user. `event` holds the browsers keyboard event, `handler` passes
some additional information to handle the pressed key.

#### `event`

The browsers native keyboard event that gets created when the user hits a key. For a thorough documentation of this event
check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent).

#### `handler`

The `handler` holds information about the pressed key. In general, we should only need this object to handle our keyboard
events.
The most important property of the `handler` object is the `key` prop:

* `key: string` - This will hold the pressed keystroke. So if we use multiple possible keystroke combinations for the same callback
we can use this property to check which specific keystroke was pressed.

> There are more properties attached to the handle that currently don't get populated with values. So we can safely ignore those.

:::info The callback gets memoised

The callback we pass into the hook gets memoised, so every variable we reference inside the callback must be added to the
dependencies array, otherwise we will get stale values. For more on memoisation in the context of React hooks read
[this nice article](https://medium.com/@sdolidze/react-hooks-memoization-99a9a91c8853).

:::

***

### `options`

We can extensively configure how the hook behaves by passing it an `options` object. Below are all properties that the
object takes.

```js
// Default values
const options = {
  filter: undefined,
  filterPreventDefault: true,
  enableOnTags: [],
  enabled: true,
  splitKey: '+',
  keyup: undefined,
  keydown: true
};
```

```ts
// Type Definitions
type AvailableTags = 'INPUT' | 'TEXTAREA' | 'SELECT';

type Options = {
  enabled?: boolean;
  filter?: (event: KeyboardEvent) => boolean;
  filterPreventDefault?: boolean;
  enableOnTags?: AvailableTags[];
  enableOnContentEditable?: boolean;
  splitKey?: string;
  keyup?: boolean;
  keydown?: boolean;
};
```

#### Properties

##### `filter`

```ts
filter: (event: KeyboardEvent) => boolean // default: undefined
```

Pass a function `filter` to determine if the callback should get triggered. Return `false` to __prevent__ the execution
of the callback and `true` to __allow__ the callback to be triggered.

##### `filterPreventDefault`

```ts
filterPreventDefault: boolean // default: true
```

This flag determines if the default browser behavior should be prevented if the given `filter` function returns `false` and
therefore prevents the execution of the callback. `true` is the default value, so each time the filter blocks the callback
execution, the browser won't proceed with its default behavior. Setting this to `false` will allow the browser to proceed
with the default behavior.

A good example for this behavior is the override of `command+s`, which normally triggers a save page dialog
inside the browser.

```js
useHotkeys('command+s', someCallback, {
  // This will allow the browser to show the save page dialog.
  // Setting it to true will prevent that.
  filterPreventDefault: false,
  filter: () => false
});
```

##### `enabledOnTags`

```ts
enabledOnTags: string[] // default: undefined
```

Normally we do not want a hotkey being triggered while a user types something into an input field. In some cases however
this might desirable. We can enable the callback trigger for an input tag using the following values:

`INPUT`, `TEXTAREA`, `SELECT`

##### `enabled`

```ts
enabled: boolean // default: true
```

Setting this to `false` prevents the hook from doing anything.

##### `splitKey`

```ts
splitKey: string // default: "+"
```

Specifies the key that is used to combine multiple hotkeys into keystrokes. The default value is `+`, so `shift+a` triggers
when the user presses the "shift" key __and__ the "a" key.

:::danger Using "+" as a target key
Setting the `splitKey` to `-` to listen to a keystroke like `+` or `shift-+` doesn't work. There is a bug in the implementation
of the underlying hotkeys package that React Hotkeys Hook leverages. See [this issue](https://github.com/JohannesKlauss/react-hotkeys-hook/issues/544)
and [this issue](https://github.com/jaywcjlove/hotkeys/issues/270).
This problem will be addressed in the upcoming Version 4.
:::

##### `keyup`

```ts
keyup: boolean // default: false
```

Set this to `true` if we want the hook to trigger our callback on the browsers `keyUp` event.

##### `keydown`

```ts
keydown: boolean // default: true
```

Set this to `true` if we want the hook to trigger our callback on the browsers `keyDown` event. This is the default behavior.

:::info Setting `keydown` __and__ `keyup`
If we set `keyup` to `true` and __don't__ set the `keydown` prop (leaving the default), React Hotkeys Hook will assume
that we want to only listen to the browsers `keyUp` event.

If we in fact want the callback to get triggered by both events, we have to explicitly set both properties like so:

```js
useHotkeys('a', () => someCallback, {
  keydown: true,
  keyup: true
})
```

:::

***

### `deps`

```ts
deps: any[] // default: []
```

The dependency array lets we use the hook just like Reacts internal `useCallback` or `useMemo` hook. This where our
dependencies of the callback live. If for example our callback actions depend on a referentially unstable value or a
value that will change over time, we should add this value to our deps array. Check out the
[documentation part](/docs/documentation/useHotkeys/setting-callback-dependencies)
for examples.

## Return value

### `React.MutableRef<T | null>`

The `useHotkeys` hook returns a React ref. This ref by default holds the value of `null`. We can use this ref to only
trigger the hotkeys if a specific element has been focused by the user.

```jsx live
function App() {
  const [count, setCount] = useState(0);
  const ref = useHotkeys("s", () => setCount((prevCount) => prevCount + 1));

  return (
    <div>
      <div style={{ padding: "30px" }}>Count: {count}</div>
      <button style={{ padding: "30px", background: "aquamarine" }}>
        Focusing this area won't trigger the hotkey.
      </button>
      <button style={{ padding: "30px", background: "crimson" }} ref={ref}>
        Focusing this area will trigger the hotkey.
      </button>
    </div>
  );
}
```

:::info Not every html tag is able to receive focus by default
Elements that don't provide any native interactivity like `<div>`, `<span>`, `<p>`, etc. cannot receive a focus by default.
If we want to use `<div>` tags instead of the `<button>` tags in the example above we have to provide a `tabIndex` prop
to the tag. This way the focusing will work with all tags.

```jsx live
function App() {
  const [count, setCount] = useState(0);
  const ref = useHotkeys("s", () => setCount((prevCount) => prevCount + 1));

  return (
    <div>
      <div style={{ padding: "30px" }}>Count: {count}</div>
      <div style={{ padding: "30px", background: "cyan" }} tabIndex={0}>
        Focusing this area won't trigger the hotkey.
      </div>
      <div style={{ padding: "30px", background: "crimson" }} ref={ref} tabIndex={0}>
        Focusing this area will trigger the hotkey.
      </div>
    </div>
  );
}
```

:::

## Function signature overloads

There is a common case where we want to pass dependencies to the hook but no options object. Normally we would need
to write that out like this:

```js
useHotkeys('a', () => someDependency, undefined, [someDependency]);
```

To streamline this use case the hook accepts function overloads. With this
we can pass a dependency array as the third argument instead of the fourth.

```ts
function useHotkeys<T extends Element>(
  keys: string,
  callback: (event: KeyboardEvent, handler: HotkeysEvent) => void,
  deps: any[] = []
): React.MutableRef<T | null>
```

So we are able to use the hook like this:

```js
useHotkeys('a', () => someDependency, [someDependency]);
```
